/**
*  \file       mem.h
*
*  \brief      Memory Handling Functions.
*
*  \author     Michael J. Hohmann <michael.hohmann@linde-mh.de>
*              Francesco D'Aprea <francesco.daprea@linde-mh.de>
*
*  Functions:  mem_chr
*              mem_cmp
*              mem_xcmp
*              mem_cpy
*              mem_cpy_U64
*              mem_cpy_U32
*              mem_cpy_U16
*              mem_xcpy
*              mem_cpy_to_big
*              mem_cpy_to_little
*              mem_cpy_from_big
*              mem_cpy_from_little
*              mem_move
*              mem_xmove
*              mem_move_to_big
*              mem_move_to_little
*              mem_move_from_big
*              mem_move_from_little
*              mem_set
*              mem_set_U64
*              mem_set_U32
*              mem_set_U16
*              mem_swap
*/
#ifndef MEM_H_INCLUDED
#define MEM_H_INCLUDED

/*==========================================================[ public includes  ]==========================================================*/
#include "../cfg/prj.h"
#include "../cfg/mem_cfg.h"
#include "../pal/pal_endian.h"

/*==========================================================[ public defines   ]==========================================================*/

#ifdef U16_CONST
   #define MEM_U16_BYTE_CNT ((U32)(sizeof(U16)))
#endif
#ifdef U32_CONST
   #define MEM_U32_BYTE_CNT ((U32)(sizeof(U32)))
#endif
#ifdef U64_CONST
   #define MEM_U64_BYTE_CNT ((U32)(sizeof(U64)))
#endif

/*==========================================================[ public datatypes ]==========================================================*/

/*==========================================================[ public variables ]==========================================================*/

/*==========================================================[ public functions ]==========================================================*/

/** Memory Character.
*
*  The function mem_chr() searches for the passed unsigned character within the number of bytes of the memory object pointed to by p_source.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param character      Character to be searched.
*  \param number         Number of bytes to be searched.
*
*  \return               The mem_chr() function returns the pointer to the first occurrence of the character. If the character was not
*                        found it returns NIL.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_chr( const MEM_PTR_MEMORY_TYPE void * p_source
                                                    , U8                               character
                                                    , mem_number_t                     number
                                                    )
{
   MEM_PTR_MEMORY_TYPE U8 * p_src = (MEM_PTR_MEMORY_TYPE U8 *)p_source;

   while ( (*p_src != character) && number  )
   {
      ++p_src;
      --number;
   }

   if ( number == (mem_number_t)0 )
   {
      p_src = NIL;
   }

   return p_src;
}

/** Memory Compare.
*
*  The function mem_cmp() compares the passed number of bytes from the memory object pointed to by p_source_1 with the memory object
*  pointed to by p_source_2.
*
*  \param p_source_1  Pointer to the first memory object. It's not save to pass NIL.
*  \param p_source_2  Pointer to the second memory object. It's not save to pass NIL.
*  \param number      Number of bytes to compare.
*
*  \return  The mem_cmp() function returns an unsigned value indicating the relationship between the content of the memory objects:
*           A zero value indicates that the contents of both memory objects are equal or the passed number is zero.
*           A value greater than zero indicates that the first bytes that do not match in both objects has a greater value in p_source_1
*           than in p_source_2 if evaluated as unsigned char values; A value less than zero indicates the opposite.
*/
MEM_STATIC_INLINE S16 mem_cmp( const MEM_PTR_MEMORY_TYPE void * p_source_1
                             , const MEM_PTR_MEMORY_TYPE void * p_source_2
                             , mem_number_t                     number
                             )
{
   const MEM_PTR_MEMORY_TYPE U8 * p_src_1 = (MEM_PTR_MEMORY_TYPE U8 *)p_source_1; /* treated as unsigned value */
   const MEM_PTR_MEMORY_TYPE U8 * p_src_2 = (MEM_PTR_MEMORY_TYPE U8 *)p_source_2; /* treated as unsigned value */
   S16                            diff    = S16_CONST(0);

   while ( (diff == S16_CONST(0)) && number )
   {
      diff = (S16)*p_src_1 - (S16)*p_src_2;
      ++p_src_1;
      ++p_src_2;
      --number;
   }

   return diff;
}

/** Inverse Memory Compare.
*
*  The function mem_xcmp() compares the passed number of bytes from the memory object pointed to by p_source_1 with the memory object
*  pointed to by p_source_2 in an inverse order.
*
*  \param p_source_1  Pointer to the first memory object. It's not save to pass NIL.
*  \param p_source_2  Pointer to the second memory object. It's not save to pass NIL.
*  \param number      Number of bytes to compare.
*
*  \return  The mem_xcmp() function returns an unsigned value indicating the relationship between the content of the memory objects:
*           A zero value indicates that the contents of both memory objects are equal or the passed number is zero.
*           A value greater than zero indicates that the first bytes that do not match in both objects has a greater value in p_source_1
*           than in p_source_2 if evaluated as unsigned char values; A value less than zero indicates the opposite.
*/
MEM_STATIC_INLINE S16 mem_xcmp( const MEM_PTR_MEMORY_TYPE void * p_source_1
                              , const MEM_PTR_MEMORY_TYPE void * p_source_2
                              , mem_number_t                     number
                              )
{
   const MEM_PTR_MEMORY_TYPE U8 * p_src_1 = (const MEM_PTR_MEMORY_TYPE U8 *)p_source_1;
   const MEM_PTR_MEMORY_TYPE U8 * p_src_2 = (const MEM_PTR_MEMORY_TYPE U8 *)p_source_2;
   S16                            diff    = S16_CONST(0);

   p_src_2 += number;

   while ( (diff == S16_CONST(0)) && number )
   {
      --p_src_2;
      diff = (S16)*p_src_1 - (S16)*p_src_2;
      ++p_src_1;
      --number;
   }

   return diff;
}

/** Memory Copy.
*
*  The function mem_cpy() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by the
*  destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                    , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                    , mem_number_t                              number
                                                    )
{
   MEM_PTR_MEMORY_TYPE       U8 * p_dest = (      MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   const MEM_PTR_MEMORY_TYPE U8 * p_src  = (const MEM_PTR_MEMORY_TYPE U8 *)p_source;

   while ( number )
   {
      *p_dest = *p_src;
      ++p_dest;
      ++p_src;
      --number;
   }

   return p_destination;
}

#ifdef U64_CONST
/** Memory Copy U64.
*
*  The function mem_cpy_U64() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by the
*  destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 64 bit access.
*                        It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. Address has to be aligned properly for 64 bit access.
*                        It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_U64() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_U64(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                        , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                        , mem_number_t                              number
                                                        )
{
   MEM_PTR_MEMORY_TYPE       U64 * p_dest_U64 = (      MEM_PTR_MEMORY_TYPE U64 *)p_destination;
   const MEM_PTR_MEMORY_TYPE U64 * p_src__U64 = (const MEM_PTR_MEMORY_TYPE U64 *)p_source;

   mem_number_t left   = number % MEM_U64_BYTE_CNT;
                number = number / MEM_U64_BYTE_CNT;

   while ( number )
   {
       *p_dest_U64 = *p_src__U64;
       ++p_dest_U64;
       ++p_src__U64;
       --number;
   }

   MEM_PTR_MEMORY_TYPE       U32 * p_dest_U32 = (      MEM_PTR_MEMORY_TYPE U32 *)p_dest_U64;
   const MEM_PTR_MEMORY_TYPE U32 * p_src__U32 = (const MEM_PTR_MEMORY_TYPE U32 *)p_src__U64;

   number = left / MEM_U32_BYTE_CNT;
   left   = left % MEM_U32_BYTE_CNT;

   while ( number )
   {
       *p_dest_U32 = *p_src__U32;
       ++p_dest_U32;
       ++p_src__U32;
       --number;
   }

   MEM_PTR_MEMORY_TYPE       U16 * p_dest_U16 = (      MEM_PTR_MEMORY_TYPE U16 *)p_dest_U32;
   const MEM_PTR_MEMORY_TYPE U16 * p_src__U16 = (const MEM_PTR_MEMORY_TYPE U16 *)p_src__U32;

   number = left / MEM_U16_BYTE_CNT;
   left   = left % MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = *p_src__U16;
      ++p_dest_U16;
      ++p_src__U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE       U8 * p_dest_U8 = (      MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;
   const MEM_PTR_MEMORY_TYPE U8 * p_src__U8 = (const MEM_PTR_MEMORY_TYPE U8 *)p_src__U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = *p_src__U8;
      ++p_dest_U8;
      ++p_src__U8;
      --number;
   }

   return p_destination;
}
#endif

#ifdef U32_CONST
/** Memory Copy U32.
*
*  The function mem_cpy_U32() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by the
*  destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 32 bit access.
*                        It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. Address has to be aligned properly for 32 bit access.
*                        It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_U32() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_U32(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                        , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                        , mem_number_t                              number
                                                        )
{
   MEM_PTR_MEMORY_TYPE       U32 * p_dest_U32 = (      MEM_PTR_MEMORY_TYPE U32 *)p_destination;
   const MEM_PTR_MEMORY_TYPE U32 * p_src__U32 = (const MEM_PTR_MEMORY_TYPE U32 *)p_source;

   mem_number_t left   = number % MEM_U32_BYTE_CNT;
                number = number / MEM_U32_BYTE_CNT;

   while ( number )
   {
       *p_dest_U32 = *p_src__U32;
       ++p_dest_U32;
       ++p_src__U32;
       --number;
   }

   MEM_PTR_MEMORY_TYPE       U16 * p_dest_U16 = (      MEM_PTR_MEMORY_TYPE U16 *)p_dest_U32;
   const MEM_PTR_MEMORY_TYPE U16 * p_src__U16 = (const MEM_PTR_MEMORY_TYPE U16 *)p_src__U32;

   number = left / MEM_U16_BYTE_CNT;
   left   = left % MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = *p_src__U16;
      ++p_dest_U16;
      ++p_src__U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE       U8 * p_dest_U8 = (      MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;
   const MEM_PTR_MEMORY_TYPE U8 * p_src__U8 = (const MEM_PTR_MEMORY_TYPE U8 *)p_src__U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = *p_src__U8;
      ++p_dest_U8;
      ++p_src__U8;
      --number;
   }

   return p_destination;
}
#endif

#ifdef U16_CONST
/** Memory Copy U16.
*
*  The function mem_cpy_U16() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by the
*  destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 16 bit access.
*                        It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. Address has to be aligned properly for 16 bit access.
*                        It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_U16() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_U16(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                        , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                        , mem_number_t                              number
                                                        )
{
   MEM_PTR_MEMORY_TYPE       U16 * p_dest_U16 = (      MEM_PTR_MEMORY_TYPE U16 *)p_destination;
   const MEM_PTR_MEMORY_TYPE U16 * p_src__U16 = (const MEM_PTR_MEMORY_TYPE U16 *)p_source;

   mem_number_t left   = number % MEM_U16_BYTE_CNT;
                number = number / MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = *p_src__U16;
      ++p_dest_U16;
      ++p_src__U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE       U8 * p_dest_U8 = (      MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;
   const MEM_PTR_MEMORY_TYPE U8 * p_src__U8 = (const MEM_PTR_MEMORY_TYPE U8 *)p_src__U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = *p_src__U8;
      ++p_dest_U8;
      ++p_src__U8;
      --number;
   }

   return p_destination;
}
#endif

/** Inverse Memory Copy.
*
*  The function mem_xcpy() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by the
*  destination pointer with an inverse order. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_xcpy() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_xcpy(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                     , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                     , mem_number_t                              number
                                                     )
{
   MEM_PTR_MEMORY_TYPE       U8 * p_dest = (      MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   const MEM_PTR_MEMORY_TYPE U8 * p_src  = (const MEM_PTR_MEMORY_TYPE U8 *)p_source;

   p_dest += number;

   while ( number )
   {
      --p_dest;
      *p_dest = *p_src;
      ++p_src;
      --number;
   }
   return p_destination;
}

/** Memory Copy To Big Endian.
*
*  The function mem_cpy_to_big() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by 
*  the destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_to_big() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_to_big(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                           , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                           , mem_number_t                              number
                                                           )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_cpy( p_destination, p_source, number );
#else
   return mem_xcpy( p_destination, p_source, number );
#endif
}

/** Memory Copy To Little Endian.
*
*  The function mem_cpy_to_little() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by
*  the destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_to_little() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_to_little(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                              , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                              , mem_number_t                              number
                                                              )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_xcpy( p_destination, p_source, number );
#else
   return mem_cpy( p_destination, p_source, number );
#endif
}

/** Memory Copy From Big Endian.
*
*  The function mem_cpy_from_big() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to by
*  the destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_from_big() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_from_big(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                             , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                             , mem_number_t                              number
                                                             )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_cpy( p_destination, p_source, number );
#else
   return mem_xcpy( p_destination, p_source, number );
#endif
}

/** Memory Copy From Little Endian.
*
*  The function mem_cpy_from_little() copies a number of bytes from the memory pointed to by the source pointer into the memory pointed to 
*  by the destination pointer. If copying takes place between memory objects that overlap, the behaviour is undefined.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to copy.
*
*  \return               The mem_cpy_from_big() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_cpy_from_little(       MEM_PTR_MEMORY_TYPE void * RESTRICT p_destination
                                                                , const MEM_PTR_MEMORY_TYPE void * RESTRICT p_source
                                                                , mem_number_t                              number
                                                                )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_xcpy( p_destination, p_source, number );
#else
   return mem_cpy( p_destination, p_source, number );
#endif
}

/** Memory Move.
*
*  The function mem_move() moves a number of bytes from the source address to the destination address.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to be moved.
*
*  \return               The mem_move() function returns the destination pointer.
*
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_move( MEM_PTR_MEMORY_TYPE void * p_destination
                                                     , MEM_PTR_MEMORY_TYPE void * p_source
                                                     , mem_number_t               number
                                                     )
{
   MEM_PTR_MEMORY_TYPE U8 * p_dest = (MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   MEM_PTR_MEMORY_TYPE U8 * p_src  = (MEM_PTR_MEMORY_TYPE U8 *)p_source;

   if ( p_dest > p_src )
   {
       p_src  += number;
       p_dest += number;

       while ( number )
       {
           --p_dest;
           --p_src;
           *p_dest = *p_src;
           --number;
       }
   }
   else if ( p_dest < p_src )
   {
       while ( number )
       {
           *p_dest = *p_src;
           ++p_dest;
           ++p_src;
           --number;
       }
   }
   else
   {
       /* nothing to do */
   }

   return p_destination;
}

/** Inverse Memory Move.
*
*  The function mem_xmove() moves and swaps a number of bytes from the source address to the destination address.
*
*  \param p_destination Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source      Pointer to the source memory object. It's not save to pass NIL.
*  \param number        Number of bytes to be moved inverse.
*
*  \return The mem_xmove() function returns the destination pointer.
*
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_xmove( MEM_PTR_MEMORY_TYPE void * p_destination
                                                      , MEM_PTR_MEMORY_TYPE void * p_source
                                                      , mem_number_t               number
                                                      )
{
   MEM_PTR_MEMORY_TYPE U8 * p_dest     = (MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   MEM_PTR_MEMORY_TYPE U8 * p_src      = (MEM_PTR_MEMORY_TYPE U8 *)p_source;

   mem_number_t             cpy_num    = (mem_number_t)0;
   MEM_PTR_MEMORY_TYPE U8 * p_cpy_dest = NIL;
   MEM_PTR_MEMORY_TYPE U8 * p_cpy_src  = NIL;

   mem_number_t             swp_num    = (mem_number_t)0;
   MEM_PTR_MEMORY_TYPE U8 * p_swp_dest = NIL;
   MEM_PTR_MEMORY_TYPE U8 * p_swp_src  = NIL;

   if ( p_dest >= p_src )
   {
      cpy_num = ( p_dest - p_src );
      if ( cpy_num < number )
      {
         swp_num = ( number - cpy_num );
      }
      else
      {
         cpy_num = number;
      }
      p_cpy_src  = p_src;
      p_cpy_dest = p_dest + number;
      p_swp_src  = p_src  + cpy_num;
      p_swp_dest = p_dest + swp_num;
   }
   else
   {
      cpy_num = ( p_src - p_dest );
      if ( cpy_num < number )
      {
         swp_num = ( number - cpy_num );
      }
      else
      {
         cpy_num = number;
      }
      p_cpy_src  = p_src  + swp_num;
      p_cpy_dest = p_dest + cpy_num;
      p_swp_src  = p_src;
      p_swp_dest = p_dest + number;
   }

   /* copy data */
   while ( cpy_num )
   {
      --p_cpy_dest;
      *p_cpy_dest = *p_cpy_src;
      ++p_cpy_src;
      --cpy_num;
   }

   {
      /* swap data */
      U8 swp_buffer = *p_swp_src;

      swp_num /= (mem_number_t)2;

      while ( swp_num )
      {
         --p_swp_dest;
         *p_swp_src  = *p_swp_dest;
         *p_swp_dest = swp_buffer;
         ++p_swp_src;
         swp_buffer = *p_swp_src;
         --swp_num;
      }
   }

   return p_destination;
}

/** Memory Move To Big Endian.
*
*  The function mem_move_to_big() moves a number of bytes from the source address to the destination address.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to be moved.
*
*  \return               The mem_move_to_big() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_move_to_big( MEM_PTR_MEMORY_TYPE void * p_destination
                                                            , MEM_PTR_MEMORY_TYPE void * p_source
                                                            , mem_number_t               number
                                                            )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_move( p_destination, p_source, number );
#else
   return mem_xmove( p_destination, p_source, number );
#endif
}

/** Memory Move To Little Endian.
*
*  The function mem_move_to_little() moves a number of bytes from the source address to the destination address.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to be moved.
*
*  \return               The mem_move_to_little() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_move_to_little( MEM_PTR_MEMORY_TYPE void * p_destination
                                                               , MEM_PTR_MEMORY_TYPE void * p_source
                                                               , mem_number_t               number
                                                               )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_xmove( p_destination, p_source, number );
#else
   return mem_move( p_destination, p_source, number );
#endif
}

/** Memory Move From Big Endian.
*
*  The function mem_move_from_big() moves a number of bytes from the source address to the destination address.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to be moved.
*
*  \return               The mem_move_from_big() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_move_from_big( MEM_PTR_MEMORY_TYPE void * p_destination
                                                              , MEM_PTR_MEMORY_TYPE void * p_source
                                                              , mem_number_t               number
                                                              )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_move( p_destination, p_source, number );
#else
   return mem_xmove( p_destination, p_source, number );
#endif
}

/** Memory Move From Little Endian.
*
*  The function mem_move_from_little() moves a number of bytes from the source address to the destination address.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param p_source       Pointer to the source memory object. It's not save to pass NIL.
*  \param number         Number of bytes to be moved.
*
*  \return               The mem_move_from_little() function returns the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_move_from_little( MEM_PTR_MEMORY_TYPE void * p_destination
                                                                 , MEM_PTR_MEMORY_TYPE void * p_source
                                                                 , mem_number_t               number
                                                                 )
{
#if PAL_ENDIAN == PAL_ENDIAN_BIG
   return mem_xmove( p_destination, p_source, number );
#else
   return mem_move( p_destination, p_source, number );
#endif
}

/** Memory Set.
*
*  The function mem_set() fills the first number of bytes of the memory pointed to by p_destination with the passed character.
*
*  \param p_destination  Pointer to the destination memory object. It's not save to pass NIL.
*  \param character      Character which has to be set.
*  \param number         Number of characters that have to be set.
*
*  \return               The mem_set() function returns the value of the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_set( MEM_PTR_MEMORY_TYPE void * p_destination
                                                    , U8                         character
                                                    , mem_number_t               number
                                                    )
{
   MEM_PTR_MEMORY_TYPE U8 * p_dest = (MEM_PTR_MEMORY_TYPE U8 *)p_destination;

   while ( number )
   {
      *p_dest = character;
      ++p_dest;
      --number;
   }

   return p_destination;
}

#ifdef U64_CONST
/** Memory Set U64.
*
*  The function mem_set_U64() fills the first number of bytes of the memory pointed to by p_destination with the passed U64 value.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 64 bit access.
*                        It's not save to pass NIL.
*  \param character      Character which has to be set.
*  \param number         Number of characters that have to be set.
*
*  \return               The mem_set_U64() function returns the value of the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_set_U64( MEM_PTR_MEMORY_TYPE void * p_destination
                                                        , U8                         character
                                                        , mem_number_t               number
                                                        )
{
   const U64 value = ((U64)character << 56U)
		           | ((U64)character << 48U)
				   | ((U64)character << 40U)
				   | ((U64)character << 32U)
				   | ((U64)character << 24U)
				   | ((U64)character << 16U)
				   | ((U64)character <<  8U)
				   |  (U64)character;

   MEM_PTR_MEMORY_TYPE U64 * p_dest_U64 = (MEM_PTR_MEMORY_TYPE U64 *)p_destination;

   mem_number_t left   = number % MEM_U64_BYTE_CNT;
                number = number / MEM_U64_BYTE_CNT;

   while ( number )
   {
       *p_dest_U64 = value;
       ++p_dest_U64;
       --number;
   }

   MEM_PTR_MEMORY_TYPE U32 * p_dest_U32 = (MEM_PTR_MEMORY_TYPE U32 *)p_dest_U64;

   number = left / MEM_U32_BYTE_CNT;
   left   = left % MEM_U32_BYTE_CNT;

   while ( number )
   {
       *p_dest_U32 = (U32)value;
       ++p_dest_U32;
       --number;
   }

   MEM_PTR_MEMORY_TYPE U16 * p_dest_U16 = (MEM_PTR_MEMORY_TYPE U16 *)p_dest_U32;

   number = left / MEM_U16_BYTE_CNT;
   left   = left % MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = (U16)value;
      ++p_dest_U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE U8 * p_dest_U8 = (MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = (U8)value;
      ++p_dest_U8;
      --number;
   }

   return p_destination;
}
#endif

#ifdef U32_CONST
/** Memory Set U32.
*
*  The function mem_set_U32() fills the first number of bytes of the memory pointed to by p_destination with the passed U64 value.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 32 bit access.
*                        It's not save to pass NIL.
*  \param character      Character which has to be set.
*  \param number         Number of characters that have to be set.
*
*  \return               The mem_set_U32() function returns the value of the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_set_U32( MEM_PTR_MEMORY_TYPE void * p_destination
                                                        , U8                         character
                                                        , mem_number_t               number
                                                        )
{
   const U32 value = ((U32)character << 24U) | ((U32)character << 16U) | ((U32)character << 8U) | (U32)character;

   MEM_PTR_MEMORY_TYPE U32 * p_dest_U32 = (MEM_PTR_MEMORY_TYPE U32 *)p_destination;

   mem_number_t left   = number % MEM_U32_BYTE_CNT;
                number = number / MEM_U32_BYTE_CNT;

   while ( number )
   {
       *p_dest_U32 = value;
       ++p_dest_U32;
       --number;
   }

   MEM_PTR_MEMORY_TYPE U16 * p_dest_U16 = (MEM_PTR_MEMORY_TYPE U16 *)p_dest_U32;

   number = left / MEM_U16_BYTE_CNT;
   left   = left % MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = (U16)value;
      ++p_dest_U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE U8 * p_dest_U8 = (MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = (U8)value;
      ++p_dest_U8;
      --number;
   }

   return p_destination;
}
#endif

#ifdef U16_CONST
/** Memory Set U16.
*
*  The function mem_set_U16() fills the first number of bytes of the memory pointed to by p_destination with the passed U64 value.
*
*  \param p_destination  Pointer to the destination memory object. Address has to be aligned properly for 16 bit access.
*                        It's not save to pass NIL.
*  \param character      Character which has to be set.
*  \param number         Number of characters that have to be set.
*
*  \return               The mem_set_U16() function returns the value of the destination pointer.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_set_U16( MEM_PTR_MEMORY_TYPE void * p_destination
                                                        , U8                         character
                                                        , mem_number_t               number
                                                        )
{
   const U16 value = ((U16)character << 8U) | (U16)character;

   MEM_PTR_MEMORY_TYPE U16 * p_dest_U16 = (MEM_PTR_MEMORY_TYPE U16 *)p_destination;

   mem_number_t left   = number % MEM_U16_BYTE_CNT;
                number = number / MEM_U16_BYTE_CNT;

   while ( number )
   {
      *p_dest_U16 = value;
      ++p_dest_U16;
      --number;
   }

   MEM_PTR_MEMORY_TYPE U8 * p_dest_U8 = (MEM_PTR_MEMORY_TYPE U8 *)p_dest_U16;

   number = left;

   while ( number )
   {
      *p_dest_U8 = (U8)value;
      ++p_dest_U8;
      --number;
   }

   return p_destination;
}
#endif

/** Memory Swap.
*
*  The function mem_swap() inverts the first number of bytes of the memory object pointed to by p_destination.
*
*  \param p_destination Pointer to the memory that has to be swapped.
*  \param number        Number of byte that has to be swapped.
*
*  \return The mem_swap() function returns the pointer p_destination.
*/
MEM_STATIC_INLINE MEM_PTR_MEMORY_TYPE void * mem_swap( MEM_PTR_MEMORY_TYPE void * p_destination
                                                     , mem_number_t               number
                                                     )
{
   MEM_PTR_MEMORY_TYPE U8 * p_begin = (MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   MEM_PTR_MEMORY_TYPE U8 * p_end   = (MEM_PTR_MEMORY_TYPE U8 *)p_destination;
   U8                       buffer  = *p_begin;

   p_end += number;
   number /= (mem_number_t)2;

   while ( number )
   {
      --p_end;
      *p_begin = *p_end;
      *p_end   = buffer;
      ++p_begin;
      buffer = *p_begin;
      --number;
   }

   return p_destination;
}

/*========================================================================================================================================*/

#endif

/*******************************************************************************************************************************************
*                                 Copyright 2015, 2016, 2017 Linde Material Handling. All rights reserved.                                 *
*******************************************************************************************************************************************/
